package com.leenmvc.core.aop.proxy;

import com.leenmvc.core.dao.communal.BaseDao;
import com.leenmvc.core.dao.mysql.MysqlAfterExcute;
import com.leenmvc.core.dao.mysql.MysqlBaseDaoImpl;
import com.leenmvc.core.dao.mysql.MysqlBeforeExcute;
import com.leenmvc.core.dao.sqlServer.SqlServerAfterExcute;
import com.leenmvc.core.dao.sqlServer.SqlServerBaseDaoImpl;
import com.leenmvc.core.dao.sqlServer.SqlServerBeforeExcute;
import com.leenmvc.core.utils.ClassUtil;
import org.springframework.aop.framework.AdvisedSupport;
import org.springframework.aop.framework.AopProxy;
import org.springframework.aop.support.AopUtils;

import java.lang.reflect.*;

/**
 * 代理工具类
 */
public class ProxyUtil {

    private static BaseDao[] baseDao = new BaseDao[2];

    /**
     * 使用单例模式限制代理Dao的实例
     *
     * @return
     */
    public synchronized static BaseDao[] getProxyDao() {
        if(baseDao[0] == null || baseDao[1] == null) {
            // mysql操作的
            baseDao[0] = ProxyUtil.getProxy(new ProxyBaseDao(MysqlBaseDaoImpl.getInstance(), new MysqlBeforeExcute(), new MysqlAfterExcute()));
            baseDao[1] = ProxyUtil.getProxy(new ProxyBaseDao(SqlServerBaseDaoImpl.getInstance(), new SqlServerBeforeExcute(), new SqlServerAfterExcute()));
        }
        return baseDao;
    }

    /**
     * 获取代理类
     *
     * @param inv
     * @param <T>
     * @return
     */
    public static <T> T getProxy(InvocationHandler inv) {
        Class<?> aClass = inv.getClass();
        Field objectField = ClassUtil.getField(aClass, "object");
        Method getMethod = ClassUtil.getGetMethod(objectField);
        try {
            Object object = getMethod.invoke(inv, null);

            // 获取将被代理类实现的接口(默认第一个接口，如果不是将被代理接口写在第一位将报错)
            Class<?> aClass1 = object.getClass();
            Class<?>[] interfaces = aClass1.getInterfaces();
            if(interfaces.length > 1) {
                // LogFactory.getGlobalLog().warning("当前被代理类实现的接口为两个以上，请确保真正的被代理的接口在第一个位置！！");
            }
            T proxyBaseDao = (T)Proxy.newProxyInstance(
                    BaseDao.class.getClassLoader(),  // 类加载器
                    new Class[]{interfaces[0]},  // 数组形式的目标对象的接口类型
                    inv); // 动态处理器
            return proxyBaseDao;

        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }

        return null;
    }

    /**
     * 获取 目标对象
     * @param proxy 代理对象
     * @return 目标对象
     * @throws Exception
     */
    public static Object getTarget(Object proxy) throws Exception {
        if (!AopUtils.isAopProxy(proxy)) {
            return proxy;
        }
        if (AopUtils.isJdkDynamicProxy(proxy)) {
            proxy = getJdkDynamicProxyTargetObject(proxy);
        } else {
            proxy = getCglibProxyTargetObject(proxy);
        }
        return getTarget(proxy);
    }

    private static Object getCglibProxyTargetObject(Object proxy) throws Exception {
        Field h = proxy.getClass().getDeclaredField("CGLIB$CALLBACK_0");
        h.setAccessible(true);
        Object dynamicAdvisedInterceptor = h.get(proxy);
        Field advised = dynamicAdvisedInterceptor.getClass().getDeclaredField("advised");
        advised.setAccessible(true);
        Object target = ((AdvisedSupport) advised.get(dynamicAdvisedInterceptor)).getTargetSource().getTarget();
        return target;
    }

    private static Object getJdkDynamicProxyTargetObject(Object proxy) throws Exception {
        Field h = proxy.getClass().getSuperclass().getDeclaredField("h");
        h.setAccessible(true);
        AopProxy aopProxy = (AopProxy) h.get(proxy);
        Field advised = aopProxy.getClass().getDeclaredField("advised");
        advised.setAccessible(true);
        Object target = ((AdvisedSupport) advised.get(aopProxy)).getTargetSource().getTarget();
        return target;
    }

}
